import curses
import serial
import struct
import time
from threading import Thread

# 初始化串口配置
SERIAL_PORT = "/dev/ttyACM0"
BAUDRATE = 115200

# SBUS通道默认值（中值1024，开关量0）
CHANNEL_DEFAULTS = {
    "analog": 1024,    # 模拟通道中值
    "switch_off": 0,   # 开关关闭状态
    "switch_on": 2047  # 开关打开状态
}

class SBUSController:
    def __init__(self):
        self.ser = serial.Serial(SERIAL_PORT, baudrate=BAUDRATE,
                                bytesize=8, parity='N', stopbits=1)
        self.channels = [CHANNEL_DEFAULTS["analog"]] * 16
        self._init_switches()
        self.running = True

    def _init_switches(self):  # <- 关键方法定义
        """初始化开关通道默认状态"""
        for ch in [4, 5, 6, 7, 9]:
            self.channels[ch] = CHANNEL_DEFAULTS["switch_off"]

    def build_sbus_frame(self):
        """构建SBUS数据帧（35字节格式）"""
        frame = bytearray()
        frame.append(0x0F)  # SBUS头
        
        # 通道数据（16通道 × 2字节）
        for value in self.channels:
            frame.extend(struct.pack('>H', value))
        
        # 标志字节（默认无特殊状态）
        flags = 0x00  # 根据需求设置，如ch17激活则设为0x80
        frame.append(flags)
        
        # 计算XOR校验（覆盖0x0F后的33字节）
        checksum = 0
        for byte in frame[1:]:  # 包含通道数据32字节 + 标志1字节
            checksum ^= byte
        frame.append(checksum)
        
        return bytes(frame)

    def send_loop(self):
        """持续发送SBUS帧"""
        try:
            while self.running:
                frame = self.build_sbus_frame()
                self.ser.write(frame)
                time.sleep(0.02)  # 50Hz帧率
        except KeyboardInterrupt:
            self.close()

    def close(self):
        """关闭串口连接"""
        self.running = False
        self.ser.close()

class Interface:
    """终端控制界面"""
    KEY_BINDINGS = {
        ord('s'): ("Ch2+", 1, 50),
        ord('w'): ("Ch2-", 1, -50),
        ord('d'): ("Ch1-", 0, -50),
        ord('a'): ("Ch1+", 0, 50),
        curses.KEY_LEFT: ("Rot-", 3, -50),
        curses.KEY_RIGHT: ("Rot+", 3, 50),
        curses.KEY_UP: ("Hight+", 2, 50),
        curses.KEY_DOWN: ("Hight-", 2, -50),
        ord('1'): ("Tilt Lock", 4),
        ord('2'): ("Jump Lock", 5),
        ord('3'): ("Height Lock", 6),
        ord('4'): ("Start/Stop", 7),
        ord('0'): ("Jump", 9),
        ord('r'): ("Reset", None)
    }

    def __init__(self, controller):
        self.controller = controller
        self.stdscr = curses.initscr()
        curses.noecho()
        curses.cbreak()
        self.stdscr.keypad(True)
        self.stdscr.nodelay(True)

    def draw(self):
        """绘制界面"""
        self.stdscr.clear()
        self._draw_header()
        self._draw_channels()
        self._draw_help()
        self.stdscr.refresh()

    def _draw_header(self):
        title = "SBUS Controller - Press Q to quit"
        self.stdscr.addstr(0, 2, title, curses.A_BOLD)

    def _draw_channels(self):
        """绘制通道状态（动态布局）"""
        max_y, max_x = self.stdscr.getmaxyx()
        for i in range(16):
            x = 2 + (i // 8) * 40
            y = 2 + (i % 8) * 3
            if y + 2 < max_y:  # 确保通道信息可见
                value = self.controller.channels[i]
                label = f"Ch{i+1:02d}: {value:4d}"
                try:
                    self.stdscr.addstr(y, x, label)
                    if i in [0, 1, 2, 3]:
                        progress = int((value / 2047) * 20)
                        bar = "█" * progress + " " * (20 - progress)
                        self.stdscr.addstr(y+1, x, f"[{bar}]")
                except curses.error:
                    pass

    def _draw_help(self):
        """绘制帮助信息（动态位置）"""
        max_y, max_x = self.stdscr.getmaxyx()
        help_text = [
            "Controls:",
            "W/S - 前后 (Ch2)    A/D - 左右倾斜 (Ch1)",
            "<-/-> - 旋转 (Ch4)  ↑/↓ - 高度 (Ch3)",
            "3 - 高度调节锁(Ch7)   1 - 倾斜锁 (Ch5)",
            "4 - Start/Stop(Ch8)  2 - 跳跃锁(Ch6)",
            "0 - 跳跃(Ch10)        R - 重置所有通道"
        ]
        start_line = max(max_y - len(help_text) - 2, 0)  # 确保非负
        for i, text in enumerate(help_text):
            line = start_line + i
            if line < max_y:
                try:
                    self.stdscr.addstr(line, 2, text)
                except curses.error:
                    pass

    def handle_input(self):
        """处理键盘输入"""
        key = self.stdscr.getch()
        if key == ord('q'):
            return False
        
        if key in self.KEY_BINDINGS:
            cmd = self.KEY_BINDINGS[key]
            if cmd[0] == "Reset":
                self._reset_channels()
            elif len(cmd) == 3:  # 模拟通道
                self._adjust_analog(cmd[1], cmd[2])
            else:  # 开关通道
                self._toggle_switch(cmd[1])
        return True

    def _adjust_analog(self, ch, delta):
        """调整模拟通道"""
        new_val = self.controller.channels[ch] + delta
        self.controller.channels[ch] = max(0, min(2047, new_val))

    def _toggle_switch(self, ch):
        """切换开关通道"""
        current = self.controller.channels[ch]
        new_val = CHANNEL_DEFAULTS["switch_on"] if current == 0 else 0
        self.controller.channels[ch] = new_val

    def _reset_channels(self):
        """重置所有通道"""
        for i in range(16):
            if i in [0,1,2,3]:
                self.controller.channels[i] = CHANNEL_DEFAULTS["analog"]
            elif i in [4,5,6,7,9]:
                self.controller.channels[i] = CHANNEL_DEFAULTS["switch_off"]
            else:
                self.controller.channels[i] = 1024

    def close(self):
        """关闭界面"""
        curses.nocbreak()
        self.stdscr.keypad(False)
        curses.echo()
        curses.endwin()

def main():
    # 初始化控制器
    controller = SBUSController()
    
    # 启动发送线程
    sender_thread = Thread(target=controller.send_loop)
    sender_thread.daemon = True
    sender_thread.start()

    try:
        # 初始化界面
        ui = Interface(controller)
        while True:
            ui.draw()
            if not ui.handle_input():
                break
            time.sleep(0.05)
    finally:
        controller.close()
        ui.close()

if __name__ == "__main__":
    main()